#include "Map.h"

const int Map::step[][2] = { {0, 1}, {1, 0}, {0, -1}, {-1, 0} };

Map::Map()
{
}

// init settings
// load map
// load starting point and target point
Map::Map(bool isNew)
{
    ifstream fin("./src/map/map.res");
    if (fin.is_open()) {
        orientation = EAST;
        z = 0;

        POINT temp;
        Point tmp;
        for (int i = 0; i < MAP_MAX_HEIGHT; i++)
            for (int j = 0; j < MAP_MAX_WIDTH; j++)
                for (int k = 0; k < MAP_MAX_WIDTH; k++) {
                    fin >> map[i][j][k];
                    temp.y = j;
                    temp.x = k;
                    switch (map[i][j][k])
                    {
                    case 1:
                        upStairs.push_back(temp);
                        break;
                    case -1:
                        downStairs.push_back(temp);
                        break;
                    case 0:
                        break;
                    case 2:
                        break;
                    default:
                        tmp.x = k;
                        tmp.y = j;
                        tmp.z = i;
                        tmp.value = map[i][j][k];
                        if (tmp.value < 0)
                            portalEntrance.push_back(tmp);
                        else
                            portalExit.push_back(tmp);
                        break;
                    }
                    isVisable[i][j][k] = false;
                }

        x = downStairs[0].x;
        y = downStairs[0].y;
        observe();
    }
    else {
        cout << "Map resource file lost." << endl;
    }

    if (!isNew) {
        ifstream fin("./src/map/achieves.res");
        if (fin.is_open()) {
            for (int i = 0; i < MAP_MAX_HEIGHT; i++)
                for (int j = 0; j < MAP_MAX_WIDTH; j++)
                    for (int k = 0; k < MAP_MAX_WIDTH; k++)
                        fin >> isVisable[i][j][k];
            fin >> x;
            fin >> y;
            fin >> z;
            fin >> orientation;
        }
        else {
            cout << "Achieves file lost." << endl;
        }
    }

    // load images
    loadimage(&blank, _T("src/images/blank.jpg"));
    loadimage(&wall, _T("src/images/wall2.jpg"));
    loadimage(&downstairs, _T("src/images/downstairs.jpg"));
    loadimage(&upstairs, _T("src/images/upstairs.jpg"));

    loadimage(&player[EAST], _T("src/images/player_e2.jpg"));
    loadimage(&player[SOUTH], _T("src/images/player_s2.jpg"));
    loadimage(&player[WEST], _T("src/images/player_w2.jpg"));
    loadimage(&player[NORTH], _T("src/images/player_n2.jpg"));

    loadimage(&portalRed, _T("src/images/portal_red.jpg"));
    loadimage(&portalBlue, _T("src/images/portal_blue.jpg"));
    loadimage(&portalYellow, _T("src/images/portal_yellow.jpg"));
}

// update isVisable array 
void Map::observe()
{
	isVisable[z][y][x] = true;
	isVisable[z][y + 1][x] = true;
	isVisable[z][y - 1][x] = true;
	isVisable[z][y][x + 1] = true;
	isVisable[z][y][x - 1] = true;
}

// move forward or interact the scene
void Map::moveForward()
{
	int nexty = y + step[orientation][0];
	int nextx = x + step[orientation][1];
	if (nextx < MAP_MAX_WIDTH && nexty < MAP_MAX_WIDTH && nextx >= 0 && nexty >= 0) {
		bool footstep, door, tp;
		footstep = door = tp = false;
		switch (map[z][nexty][nextx])
		{
		case 0:
			x = nextx;
			y = nexty;
			footstep = door = true;
			break;
		case 1:
			if (z + 1 < MAP_MAX_HEIGHT) {
				z++;
				x = downStairs[z].x;
				y = downStairs[z].y;
				footstep = tp = true;
			}
			else if (z + 1 == MAP_MAX_HEIGHT ) {
				x = nextx;
				y = nexty;
				footstep = door = true;
			}
			break;
		case -1:
			if (z - 1 >= 0) {
				z--;
				x = upStairs[z].x;
				y = upStairs[z].y;
				footstep = tp = true;
			}
			else {
				x = nextx;
				y = nexty;
				footstep = door = true;
			}
			break;
		case 2:
			return;
			break;
		default:
            Point tmp;
            if (map[z][nexty][nextx] > 0)
                for (int i = 0; i < portalEntrance.size(); i++)
                    if (map[z][nexty][nextx] + portalEntrance[i].value);
                    else { tmp = portalEntrance[i];  break; }
            else
                for (int i = 0; i < portalExit.size(); i++)
                    if (map[z][nexty][nextx] + portalExit[i].value);
                    else { tmp = portalExit[i]; break; }
            
            x = tmp.x;
            y = tmp.y;
            z = tmp.z;
            footstep = tp = true;
			break;
		}
		moveNoise(footstep, door, tp);
		observe();
	}
}

void Map::moveNoise(bool footstep, bool door, bool tp)
{
	//if (footstep)
		//PlaySound(L"./src/sound/footstep.wav", NULL, SND_FILENAME | SND_ASYNC);
    // Sleep(750);

	if (door)
		PlaySound(L"./src/sound/footstep.wav", NULL, SND_FILENAME | SND_ASYNC);

	if (tp)
		PlaySound(L"./src/sound/tp.wav", NULL, SND_FILENAME | SND_ASYNC);

	// Sleep(1500);
	//if (footstep)
		//PlaySound(L"./src/sound/footstep.wav", NULL, SND_FILENAME | SND_ASYNC);
}

// turn around by change orientation
void Map::turn(Direction direct)
{
	switch (direct)
	{
	case FRONT:
		break;
	case BACK:
		orientation += 2;
		orientation %= 4;
		break;
	case LEFT:
		orientation -= 1;
		orientation += 4;
		orientation %= 4;
		break;
	case RIGHT:
		orientation += 1;
		orientation %= 4;
		break;
	default:
		break;
	}
}

// show the map on the upper right cornel of window
void Map::mapRender()
{
	static const int blockWidth = (WINDOW_WIDTH_PS - SENEN_WIDTH_PS) / MAP_MAX_WIDTH;

	setlinestyle(PS_SOLID, 0);
	for (int h = 0; h < MAP_MAX_WIDTH; h++)
		for (int l = 0; l < MAP_MAX_WIDTH; l++) {
            // calculate location
            int tx = l * blockWidth + SENEN_WIDTH_PS;
            int ty = h * blockWidth;
            if (isVisable[z][h][l]) {
                // show images
                switch (map[z][h][l])
                {
                case 0:
                    // blank
                    putimage(tx, ty, &blank);
                    break;
                case 2:
                    // wall
                    putimage(tx, ty, &wall);
                    break;
                case -1:
                    // downstairs
                    putimage(tx, ty, &downstairs);
                    break;
                case 1:
                    // upstairs
                    putimage(tx, ty, &upstairs);
                    break;
                case -3:
                case 3:
                    // portal red
                    putimage(tx, ty, &portalRed);
                    break;
                case -4:
                case 4:
                    // portal blue
                    putimage(tx, ty, &portalBlue);
                    break;
                case -5:
                case 5:
                    // portal yellow
                    putimage(tx, ty, &portalYellow);
                    break;
                }
            }
            else {
				setfillcolor(DARKGRAY);
			    fillrectangle(tx, ty, tx + blockWidth, ty + blockWidth);
            }
		}

    // show player
    putimage(x * blockWidth + SENEN_WIDTH_PS, y * blockWidth, &player[orientation]);

    // show layer
    int left = 1000, top = 380, right = 1200, bottom = 430;
    setfillcolor(LIGHTBLUE);
    fillroundrect(left, top, right, bottom, 20, 20);
    RECT rc = { left, top, right, bottom };
    setbkmode(TRANSPARENT);
    settextstyle(32, 0, _T("Courier"));
    TCHAR buf[3], str[9];
    _stprintf_s(buf, _T("%d"), z);
    str[0] = _T('\0');
    lstrcat(str, _T("Layer "));
    lstrcat(str, buf);
    drawtext(str, &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE);

    // save game button
    int tleft = 1000, ttop = 480, tright = 1200, tbottom = 530;
    fillroundrect(tleft, ttop, tright, tbottom, 20, 20);
    rc = { tleft, ttop, tright, tbottom };
    drawtext(_T("SAVE GAME"), &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}

void Map::saveAchieves()
{
    ofstream fout("./src/map/achieves.res");

    for (int i = 0; i < MAP_MAX_HEIGHT; i++) {
        for (int j = 0; j < MAP_MAX_WIDTH; j++) {
            for (int k = 0; k < MAP_MAX_WIDTH; k++)
                fout << isVisable[i][j][k] << " ";
            fout << endl;
        }
        fout << endl;
    }

    fout << x << endl;
    fout << y << endl;
    fout << z << endl;
    fout << orientation << endl;
}

bool Map::isOver()
{
	return z == MAP_MAX_HEIGHT - 1 && x == upStairs[z].x && y == upStairs[z].y;
}

int Map::getX()
{
	return x;
}

int Map::getY()
{
	return y;
}

int Map::getZ()
{
	return z;
}

int Map::getOrientation()
{
	return orientation;
}
